package com.yu.app.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.yu.admin.modules.app.service.OrderInfoAdminService;
import com.yu.app.mapper.GoodsMapper;
import com.yu.app.mapper.OrderGoodsMapper;
import com.yu.app.mapper.OrderMapper;
import com.yu.app.pojo.OrderGoods;
import com.yu.app.pojo.OrderInfo;
import com.yu.app.pojo.dto.OrderInfoDTO;
import com.yu.app.pojo.form.CreateOrderParams;
import com.yu.app.pojo.vo.GoodsVO;
import com.yu.app.pojo.vo.HistoryOrderVO;
import com.yu.app.service.OrderService;
import com.yu.common.config.property.AppConfig;
import com.yu.common.config.property.WeixinProperty;
import com.yu.common.enums.OrderStatus;
import com.yu.common.enums.OrderTakeType;
import com.yu.common.exception.ServiceException;
import com.yu.common.service.LockService;
import com.yu.common.service.RedisService;
import com.yu.common.util.GeneratorUtil;
import com.yu.common.util.ip.IpUtil;
import com.yu.common.util.servlet.ServletUtils;
import com.yu.common.util.session.SessionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    private RedisService redisService;
    @Resource
    private LockService lockService;
    @Resource
    private OrderMapper orderMapper;
    @Resource
    private OrderGoodsMapper orderGoodsMapper;
    @Resource
    private GoodsMapper goodsMapper;
    @Resource
    private WeixinProperty weixinProperty;
    @Resource
    private WxPayService wxPayService;
    @Resource
    private AppConfig appConfig;
    @Resource
    private GoodsServiceImpl goodsService;
    @Resource
    private OrderInfoAdminService orderInfoAdminService;


    // 创建订单
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String createOrder(CreateOrderParams orderParams) throws ServiceException {
        String wxOpenid = SessionUtil.getCurrentUserId();
        if (!wxOpenid.equals(orderParams.getWxOpenid())) {
            throw ServiceException.CONST_create_order_fail; // 用户的openid不对，
        }
        if (orderParams.getTotalPrice() < 0 || orderParams.getPayPrice() < 0) {
            throw ServiceException.CONST_order_price_error; // 订单价格异常，
        }
        if (CollectionUtils.isEmpty(orderParams.getGoodsVOList())) {
            throw ServiceException.CONST_order_not_has_goods; // 订单里没有商品
        }
        // 外卖配送 检验参数
        if (OrderTakeType.ENUM_take_out.text.equals(orderParams.getTakeType()))
            if (StringUtils.isEmpty(orderParams.getAddressDetail())
                    || orderParams.getAddressDetail().trim().length() < 3 // 尽量确保收货地址等正确性
                    || StringUtils.isEmpty(orderParams.getUserPhone())
                    || StringUtils.isEmpty(orderParams.getReceiver()))
                throw ServiceException.CONST_receiver_info_params_invalid;


        if (!appConfig.getShopStatus()) {
            throw ServiceException.CONST_shopkeeper_is_at_rest;  // 商家休息中，下单失败
        }
        Date date = new Date();

        int nowHour = date.getHours();
        if (!(nowHour >= appConfig.getBusinessStartTime() || nowHour < appConfig.getBusinessEndTime())) {
            throw ServiceException.CONST_it_is_not_business_hours; // 未在营业时间段
        }


        // TODO 1 redis实现的乐观锁，防止重复下单和频繁下单
        String version = System.currentTimeMillis() + "";
        if (lockService.tryLock(wxOpenid, version, 10)) {
            try {
                OrderInfo orderInfo = new OrderInfo();
                BeanUtils.copyProperties(orderParams, orderInfo);
                orderInfo.setOrderNo(GeneratorUtil.generateOrderNo());
                orderInfo.setOrderStatus(OrderStatus.ENUM_has_not_pay_money.text);
                orderInfo.setCreateTime(new Date());

                // 重新计算订单价格和生成订单相关信息
                int totalPrice = 0;
                int payPrice = 0;
                StringBuilder goodsPreview = new StringBuilder(); // eg: 霸气橙子*1, 烧仙草*2
                int goodsTotalNum = 0; // 商品的总数量
                for (GoodsVO goodsVO : orderParams.getGoodsVOList()) {
                    OrderGoods orderGoods = new OrderGoods();
                    orderGoods.setOrderNo(orderInfo.getOrderNo());
                    orderGoods.setGoodsId(goodsVO.getId());
                    orderGoods.setGoodsName(goodsVO.getName());
                    orderGoods.setGoodsProperty(goodsVO.getPropertyStr());
                    orderGoods.setBuyNum(goodsVO.getNumber());
                    orderGoods.setRealPrice(goodsVO.getRealPrice());
                    // 设置订单的商品信息
                    if (StringUtils.isEmpty(goodsVO.getPropertyStr())) {
                        goodsPreview.append(goodsVO.getName()).append("*").append(goodsVO.getNumber()).append(",");
                    } else {
                        goodsPreview.append(goodsVO.getName()).append("[").append(goodsVO.getPropertyStr()).append(("]"))
                                .append("*").append(goodsVO.getNumber()).append(",");
                    }
                    goodsTotalNum += goodsVO.getNumber();
                    totalPrice += goodsVO.getRealPrice() * goodsVO.getNumber(); // TODO 这里可以去数据库里找对应的realPrice
                    payPrice += goodsVO.getRealPrice() * goodsVO.getNumber();
                    orderGoodsMapper.insert(orderGoods);
                    goodsMapper.increaseSellCountById(goodsVO.getId(), goodsVO.getNumber());  // 更新商品的月销量
                }
                goodsPreview.deleteCharAt(goodsPreview.length() - 1);
                orderInfo.setGoodsPreview(goodsPreview.toString());
                orderInfo.setGoodsTotalNum(goodsTotalNum);

                if (OrderTakeType.ENUM_take_out.text.equals(orderParams.getTakeType())) {
                    totalPrice += appConfig.getPackingPrice();
                    totalPrice += appConfig.getSendingPrice();
                    payPrice += appConfig.getPackingPrice();
                    payPrice += appConfig.getSendingPrice();
                }

                if (totalPrice != orderParams.getTotalPrice() || payPrice != orderParams.getTotalPrice())
                    throw ServiceException.CONST_order_price_error;

                orderInfo.setPayPrice(null); // 实际支付价格用微信回调去调
                orderInfo.setTotalPrice(totalPrice);
                orderMapper.insert(orderInfo);
                return orderInfo.getOrderNo();
            } catch (Exception e) { // 下单失败就释放锁 成功的话就让锁自然过期 避免重复下单或频繁下单
                lockService.releaseLock(wxOpenid); // TODO 2 可以使用Lua脚本实现带版本的释放锁，确保释放锁的那个人是他自己
                throw e;
            }
        } else {
            throw ServiceException.CONST_repeat_create_order;
        }
    }


    /**
     * 通过微信通用下单接口API成功获取预支付交易会话标识（prepay_id）后，需要通过小程序调起支付API来调起微信支付收银台
     *
     * @param ip 小程序调起收银台
     *           wx.requestPayment
     *           ({
     *           "timeStamp": "1414561699",
     *           "nonceStr": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
     *           "package": "prepay_id=wx201410272009395522657a690389285100",
     *           "signType": "RSA",
     *           "paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg==",
     *           "success":function(res){},
     *           "fail":function(res){},
     *           "complete":function(res){}
     *           })
     */
    // 微信小程序预先支付的统一订单接口，返回的微信支付需要的参数, TODO 可以通过自己的orderNo去查询有没有支付  回调或定时任务去轮询
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Object wxPrepay(String orderNo, String ip) throws ServiceException {
        OrderInfo orderInfo = orderMapper.selectById(orderNo);
        try {
            WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
            // 设置微信请求基本信息
            orderRequest.setAppid(weixinProperty.getAppid());
            orderRequest.setMchId(weixinProperty.getMchId());
            orderRequest.setOutTradeNo(orderNo); // 商户系统内部订单号，只能是数字、大小写字母_-*且在同一个商户号下唯一
            orderRequest.setBody(appConfig.getShopName()); // 示例值：Image形象店-深圳腾大-QQ公仔
            Date expireDate = new Date(System.currentTimeMillis() + 24 * 3600 * 1000);
            orderRequest.setTimeExpire(new SimpleDateFormat("yyyyMMddHHmmss").format(expireDate));
//            orderRequest.setTimeExpire("20220404180021"); // TODO 测试 // TODO
            System.out.println("微信支付订单过期时间 expireTime=" + orderRequest.getTimeExpire());
            orderRequest.setTotalFee(orderInfo.getTotalPrice()); // 订单总金额，单位为分
            orderRequest.setOpenid(SessionUtil.getCurrentUserWxOpenid());
            orderRequest.setSpbillCreateIp(IpUtil.getIp(ServletUtils.getRequest())); // APP和网页支付提交用户端ip
            orderRequest.setTradeType(WxPayConstants.TradeType.JSAPI); // JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
            return wxPayService.createOrder(orderRequest);
        } catch (WxPayException e) {
            log.error("[微信支付] 异常", e);
            throw ServiceException.CONST_weixin_pay_exception;
        } catch (Exception e) {
            log.error("[预付款异常]", e);
            throw ServiceException.CONST_weixin_pay_exception;
        }
    }

    @Override
    public Page<HistoryOrderVO> getHistoryOrderByPage(Integer pageNo, Integer pageSize) throws ServiceException {
        String wxOpenid = SessionUtil.getCurrentUserWxOpenid();
        Page<HistoryOrderVO> page = new Page<>(pageNo, pageSize);
        page.setRecords(orderMapper.getHistoryOrderByPage(wxOpenid, (pageNo - 1) * pageSize, pageSize));
        page.setTotal(orderMapper.getHistoryOrderTotalCount(wxOpenid));
        return page;
    }

    @Override
    public OrderInfoDTO getOrderDetail(String orderNo) throws ServiceException {
        OrderInfoDTO orderInfoDTO = new OrderInfoDTO();
        OrderInfo orderInfo = orderMapper.selectById(orderNo);
        BeanUtils.copyProperties(orderInfo, orderInfoDTO);
        orderInfoDTO.setOrderGoodsList(goodsService.getOrderGoodsList(orderNo));
        return orderInfoDTO;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public Integer cancelOrder(String orderNo) throws ServiceException {
        OrderInfo orderInfo = orderMapper.selectById(orderNo);
        if (orderInfo == null)
            return 0;

        // 只有在真正未付款的时候才能取消订单
        if (!orderInfo.getWxOpenid().equals(SessionUtil.getCurrentUserId())
                || !orderInfo.getOrderStatus().equals(OrderStatus.ENUM_has_not_pay_money.text))
            throw ServiceException.CONST_can_not_cancel_order;

        try {
            orderInfoAdminService.queryWeixinOrder(orderNo);
        } catch (WxPayException e) {
            e.printStackTrace();
        }

        QueryWrapper<OrderGoods> wrapper = new QueryWrapper<OrderGoods>().eq("order_no", orderNo);
        // 将月售量减回去
        List<OrderGoods> orderGoods = orderGoodsMapper.selectList(wrapper);
        for (OrderGoods orderGood : orderGoods)
            goodsMapper.increaseSellCountById(orderGood.getGoodsId(), -orderGood.getBuyNum());
        orderInfo.setOrderStatus(OrderStatus.ENUM_has_canceled.text);
        return orderMapper.updateById(orderInfo);
    }

    // 用户确认收货
    @Transactional
    @Override
    public Integer finishedOrder(String orderNo) throws ServiceException {
        OrderInfo orderInfo = orderMapper.selectById(orderNo);
        // '请取餐' 或 '已送达' 时用户才能让去确认收货
        if (orderInfo.getWxOpenid().equals(SessionUtil.getCurrentUserId())
                || orderInfo.getOrderStatus().equals(OrderStatus.ENUM_please_take_meal.text)
                || orderInfo.getOrderStatus().equals(OrderStatus.ENUM_has_received.text))
            return orderMapper.updateOrderStatus(orderNo, OrderStatus.ENUM_has_completed.text);

        throw ServiceException.CONST_confirm_receive_failed;
    }

    // 获取正在处理的订单
    @Override
    public List<OrderInfoDTO> getHandlingOrders() throws ServiceException {
        String wxOpenid = SessionUtil.getCurrentUserWxOpenid();
        List<String> params = new ArrayList<String>() {{
            add(OrderStatus.ENUM_has_completed.text);
            add(OrderStatus.ENUM_has_canceled.text);
            add(OrderStatus.ENUM_has_refunded.text);
            add(OrderStatus.ENUM_on_refunding.text);
        }};
        List<OrderInfo> orderInfos = orderMapper.selectList(new QueryWrapper<OrderInfo>()
                .eq("wx_openid", wxOpenid)
                .notIn("order_status", params)
                .orderByDesc("create_time").last(" limit  5"));

        List<OrderInfoDTO> list = new ArrayList<>();
        for (OrderInfo orderInfo : orderInfos) {
            OrderInfoDTO orderInfoDTO = new OrderInfoDTO();
            BeanUtils.copyProperties(orderInfo, orderInfoDTO);
            orderInfoDTO.setOrderGoodsList(goodsService.getOrderGoodsList(orderInfo.getOrderNo()));
            list.add(orderInfoDTO);
        }
        return list;
    }
}
